﻿#include <QDebug>
#include "expression.h"
#include "sentence.h"
#include "literals/bracket.h"

Expression::Expression()
  {
  }

bool Expression::operator==(const Expression &other) const
  {
  if (size() != other.size())
    return false;
  for (size_t i = 0; i < size(); i++)
    if (at(i) != other.at(i))
      return false;
  return true;
  }

Word const &Expression::wordAt(QPair<int, int> position) const
  {
  return *at(position.first).at(position.second);
  }

void Expression::concatenate(Expression* other)
  {
  copyExpressionToMe(other, size());
  }

void Expression::removeWords(int startingSentence, int startingWord, int wordCount)
  {
  wordCount =- (*this)[startingSentence].removeWords(startingWord, wordCount);
  size_t i = 1;
  while ((wordCount > 0) && (size() > i + startingSentence ))
    {
    wordCount =- (*this)[startingSentence + i].removeWords(0, wordCount);
    i++;
    }

  if (wordCount > 0)
    qWarning() << Q_FUNC_INFO << "Expression has less words than you wanted to delete";
  }

// if expression consists of multiple sentences, it should be inserted as a bracket
void Expression::insertIntoSentence(int startingSentence, int startingWord, Expression const & expression)
  {
  if (expression.size())
    {
    Bracket *bracket = new Bracket(expression);
    (*this)[startingSentence].insert(startingWord, bracket);
    }
  else
    (*this)[startingSentence].appendWordsFromSentence(expression.at(0), startingWord, expression.at(0).size());
  }

void Expression::copyExpressionToMe(Expression *newE, int i)
  {
  for (size_t j = 0; j < newE->size(); j++)
    {
    insert(begin() + i, Sentence(newE->at(j)));
    i++;
    }
  }

bool Expression::groupTypes()
  {
  bool changed = false;
  for (size_t i = 0; i < size(); i++)
    changed |= (*this)[i].groupTypes();
  return changed;
  }

bool Expression::doDeltaTricks()
  {
  bool changed = false;
  for (size_t i = 0; i < size(); i++)
    changed |= (*this)[i].doDeltaTrick();
  return changed;
  }

bool Expression::sameSentencesArithmetic()
  {
  bool found = false;
  for (size_t i = 0; i < size(); i++)
    {
    for (size_t j = 0; j < size(); j++)
      {
      if (j == i) // do not check sentence with itself
        continue;
      if ((*this)[i].compareWithoutCoefficient((*this)[j])) // two same sentence found. We will add them
        {
        (*this)[i].setCoefficient((*this)[i].getCoefficient() + (*this)[j].getCoefficient());
        erase(begin() + j); // removeAt(j);
        if  (0 == (*this)[i].getCoefficient()) // remove second sentence if needed
          erase(begin() + i);
        found = true;
        j--;
        continue;
        }
      }
    }
  return found;
  }

bool Expression::multiplyAllBrackets()
  {
  if (countWordType(Word::WT_BRACKET) == 0)
    return false;
  while (countWordType(Word::WT_BRACKET) > 0)
    {
    for (size_t i = 0; i < size(); i++)
      {
      if (at(i).countWordType(Word::WT_BRACKET) == 0)
        continue;
        // we have a sentence with brackets
        // now we need to divede it
        /*
         * There are some scenarious that we have to think about
         *   A) Bracket
         *   B) X * Bracket
         *   C) Bracekt * X
         *   D) X * Bracket * X
         * where X is a subsentence
         *
         * The best way to know with which scenario we have to deal
         * is to check the index of the bracket (is it the first word?) and
         * then to check if there is something after the bracket.
         *
         * A: remove Bracket from _exp;  _exp.insert( expression from bracket );
         * B: extract X; bracket->multiplyWith(X); remove Bracket*X from _exp; insert result  to _exp
         * C: extract X; X->multiplyWith(bracket); remove X*Bracket from _exp; insert result  to _exp
         * D: As B; Actually, if we handle it delicately in B, we coud ignore this scenario.
         */
      for (int j = 0; j < at(i).size(); j++)
        {
        if (at(i).at(j)->getType() == Word::WT_BRACKET)
          {
          const Bracket &bracket = static_cast<const Bracket&>(*at(i).at(j));
          Expression expFromBracket = bracket; // we are making a copy on which we will work; we don't want to change original just yet.
          if (j == 0)
            {
            // bracket is first word in sentence
            if (at(i).size() == 1) // there is only the bracket in this sentence
              { // scenario A
              expFromBracket.multiplyByNumber(at(i).getCoefficient() );
              erase(begin() + i); // removeAt(i); // this deletes our Bracket *b here!
              copyExpressionToMe(&expFromBracket, i);
              }
            else
              { // scenario C
              Expression X;
              Sentence s;
              int howManyWordsAreInX = 0;
              for (int k = j+1; k < at(i).size(); k++)
                {
                howManyWordsAreInX++;
                s.insert(s.size(), at(i).at(k)->createCopy());
                }
              X.push_back(std::move(s));  // we don't need s anymore
              Expression multiplicaionResult;
              expFromBracket.multiplyWith(&X, multiplicaionResult);
              (*this)[i].removeWords(j, howManyWordsAreInX+1);
              Bracket *newBracket = new Bracket(multiplicaionResult);
              (*this)[i].insert(j, newBracket);
              }
            } // if (j == 0)
          else
            { // scenario B (possibly D)
            Expression X; // this is an expression that will hold s.
            Sentence s; // we will fill this with all the Words that are before bracket
            int howManyWordsAreInX = 0;
            for (int k = 0; k < j; k++)
              {
              howManyWordsAreInX++;
              s.insert(s.size(), (*this)[i].at(k)->createCopy());
              }
            X.push_back(std::move(s));  // we don't need s anymore
            Expression multiplicationResult;
            X.multiplyWith(&expFromBracket, multiplicationResult);
            (*this)[i].removeWords(0, howManyWordsAreInX+1);
            Bracket *newBracket = new Bracket(multiplicationResult);
            (*this)[i].insert(0, newBracket);
            }
          } // if (_exp.at(i).at(j).getType() == Word::WT_BRACKET)
        } // for
      }
    }
  return true;
  }

void Expression::multiplyByNumber(int n)
  {
  for (size_t i = 0; i < size(); i++)
    (*this)[i].setCoefficient( (*this)[i].getCoefficient() * n );
  }

void Expression::multiplyWith(Expression * other, Expression &result) const
  {
  /*
   * (a)*(a+b) = aa + ab
   * (a+b)*a = aa + ba
   * (a+b)*(c+d) = ac + bc + ad + bd
   * (2a+3b)*(5c-7d) = 10ac + 15bc - 14ad - 21bd
   *
   * Dla każdego zdania S u nas
   *   dla każdego zdania oS w other
   *     skonkatenuj S i oS i zapisz wynik w rS
   *     przemnóż współczynniki S i oS i ustaw wynik w rS
   *     dodaj rS do result.
   */
  result.clear();
  for (size_t i = 0; i < other->size(); i++)
    {
    for (size_t j = 0; j < this->size(); j++)
      {
      Sentence rS(this->at(j));
      rS.concatenate(other->at(i)); // this appends words and takes care of coefficient
      result.push_back(rS);
      }
    }
  }

int Expression::countWordType(Word::Type type) const
  {
  int nType = 0;
  for (size_t i = 0; i < size(); i++)
    nType += (*this)[i].countWordType(type);
  return nType;
  }

int Expression::countWords() const
  {
  int nWords = 0;
  for (size_t i = 0; i < size(); i++)
    nWords += (*this)[i].size();
  return nWords;
  }

/*
 *@param  - startingSentence is integer from which sentence inside expression data will be copied
 *          wordCount - how many word should be copied from other expression
 *@brief  - this function creates new object of expression based on this(on which was called), which should be released by caller.
 *          This new object is part of other Expression. Analogy to Substring.
 */
void Expression::subExpression(int startingSentence, int startingWord, int wordCount, Expression &subExp) const
  {
  for (size_t i = startingSentence; (i < this->size()) && (wordCount > 0); i++)
    {
    Sentence s;
    for (int j = startingWord; (j < (*this)[i].size()) && (wordCount > 0); j++, wordCount--)
      s.insert(s.size(), (*this)[i].at(j)->createCopy());
    subExp.push_back(std::move(s));
    }
  }

void Expression::sort()
  {
  qSort(begin(), end(), &Expression::sentenceLessThan);
  for (size_t i = 0; i < size(); i++)
    (*this)[i].groupTypes();
  }

bool Expression::factorOutWhatYouCan()
  {
  bool changed = true;

  while (changed != false)
    {
    changed = false;
    changed |= factorOutCommonSentence();

    if (countWordType(Word::WT_OPERATOR))
      changed |= factorOutType(Word::WT_OPERATOR);
    else if (countWordType(Word::WT_FUNCTION))
      changed |= factorOutType(Word::WT_FUNCTION);
    else if (countWordType(Word::WT_DELTA))
      changed |= factorOutType(Word::WT_DELTA);
    else if (countWordType(Word::WT_VARIABLE))
      changed |= factorOutType(Word::WT_VARIABLE);
    }

  for (size_t i = 0; i < size(); i++)
    for (int j = 0; j < (*this)[i].size(); j++)
      if ((*this)[i].at(j)->getType() == Word::WT_BRACKET)
        changed |= dynamic_cast<Bracket&>(*(*this)[i].at(j)).factorOutWhatYouCan();

  return changed;
  }

// if there is the same subsentence in every sentence, we should factor it out
// one exception: we can't factor out operators.
bool Expression::factorOutCommonSentence()
  {
  bool changed = true;
  Sentence commonSentence((*this)[0]);
  for (size_t i = 1; i < size(); i++)
    {
    (*this)[i].getCommonPart(commonSentence, commonSentence);
    if (commonSentence.size() == 0)
      changed = false;
    }

  // we have to ignore any operators in commonSentence
  for (int i = commonSentence.size()-1; i >= 0; i--)
    if (commonSentence.at(i)->getType() == Word::WT_OPERATOR)
      commonSentence.removeWords(i, 1);

  if ((size() < 2) || (commonSentence.size() < 1)) // otherwise it will try to factor out everything a_i = a_i (1) = a_i (1 (1)) etc
    changed = false;

  if (changed)
    {
    // we need to remove commonSentence from each sentence in _exp
    for (size_t i = 0; i < size(); i++)
      {
      Sentence tmp;
      int startingPoint = (*this)[i].getCommonPart(commonSentence, tmp);
      (*this)[i].removeWords(startingPoint, commonSentence.size());
      (*this)[i].setCoefficient((*this)[i].getCoefficient() / commonSentence.getCoefficient());
      }

    // now we need to put current _expression in a Bracket
    // this Bracket in a NewSentence
    // add second bracket with common part
    // behold! new expression is here
    Sentence &sCommonPart = commonSentence;
    Bracket *bracket = new Bracket(*this);
    sCommonPart.insert(sCommonPart.size(), bracket);

    clear();
    push_back(std::move(sCommonPart));
    }

  // we should also work on every expression in brackets
  for (size_t i = 0; i < size(); i++)
    for (int j = 0; j < (*this)[i].size(); j++)
      if ((*this)[i].at(j)->getType() == Word::WT_BRACKET)
        changed |= dynamic_cast<Bracket&>(*(*this)[i].at(j)).factorOutCommonSentence();

  return changed;
  }

// words of a given type can be factored out
// only if they are identical in both sentences
// e.g.
//      a b_i + a c_i        OK
//      a b_i + a c          NO
bool Expression::factorOutType(Word::Type type)
  {
  bool changed = true;
  QScopedArrayPointer<QList<int>> sameParts(new QList<int>[size()]);

  for (size_t i = 0; i < size(); i++)
    {
    sameParts[i].append(i);
    Sentence s1 = (*this)[i].getSubsentenceOfType(type);
    if (s1.size() == 0)
      continue;
    for (size_t j = i+1; j < size(); j++)
      {
      Sentence s2 = (*this)[j].getSubsentenceOfType(type);
      if (s1.compareWithoutCoefficient(s2))
        sameParts[i].append(j);
      }
    }

  int max = -1, maxIndex = 0;
  for (size_t i = 0; i < size(); i++)
    {
    if (sameParts[i].size() > max)
      {
      max = sameParts[i].size();
      maxIndex = i;
      }
    }

  if (max > 1)
    {
    QList<int> sentencesToBeStripped = sameParts[maxIndex];

    // let's move our sentences to the front
    if (sentencesToBeStripped.size() < size())
      {
      // tricky, because after each move, indexes change
      QList<Sentence> tmp;
      for (int i = 0; i < sentencesToBeStripped.size(); i++)
        tmp.append((*this)[sentencesToBeStripped[i]]);
      for (int i = sentencesToBeStripped.size()-1; i >= 0; i--)
        erase(begin() + sentencesToBeStripped[i]); //removeAt(sentencesToBeStripped[i]);
      for (int i = tmp.size()-1; i >= 0; i--)
        insert(begin(), std::move(tmp[i]));
      }

    // now to handle the REAL factoring

    // we need to remove theTypePart from X starting sentences
    Sentence commonPart = (*this)[0].getSubsentenceOfType(type);
    for (int i = 0; i < sentencesToBeStripped.size(); i++)
      {
      Sentence tmp;
      int startingPoint = (*this)[i].getCommonPart(commonPart, tmp);
      (*this)[i].removeWords(startingPoint, commonPart.size());
      (*this)[i].setCoefficient((*this)[i].getCoefficient() / commonPart.getCoefficient());
      }

    Sentence &sFactored = commonPart;
    Bracket *bracket = new Bracket();
    for (int i = 0; i < sentencesToBeStripped.size(); i++)
      bracket->push_back((*this)[i]);
    for (int i = 0; i < sentencesToBeStripped.size(); i++)
      erase(begin());

    sFactored.insert(sFactored.size(), bracket);

    insert(begin(), std::move(sFactored));
    } // if max > 1
  else
    changed = false;

  // we should also work on every expression in brackets
  for (size_t i = 0; i < size(); i++)
    for (int j = 0; j < (*this)[i].size(); j++)
      if ((*this)[i].at(j)->getType() == Word::WT_BRACKET)
        changed |= dynamic_cast<Bracket&>(*(*this)[i].at(j)).factorOutType(type);
  
  // consider: a_i + x a_i + b_i + y b_i
  // after factoring a_i, we should factor b_i
  if (changed)
    factorOutType(type);

  return changed;
  }

void Expression::applyLabelDictionary(QMap<QString, QStringList> const &dict)
  {
  for (size_t i = 0; i < size(); i++)
    {
    for (int j = 0; j < at(i).size(); j++)
      {
      if (at(i).at(j)->getType() == Word::WT_BRACKET)
        {
        dynamic_cast<Bracket&>(*(*this)[i].at(j)).applyLabelDictionary(dict);
        qDebug() << "something went wrong.";
        }
      QStringList labels = at(i).at(j)->getLabels();
      if (labels.size() > 1) // ah, we are handling %1.1 and so on (it's the only situation where there are more than one label)
        {
        for (int k = 0; k < labels.size(); k++)
          {
          if (dict.contains(labels[k]))\
            labels[k] = dict.value(labels[k])[0]; // <- I know this entry will have one QString only, because %1.1 corresponds to ONE label.
          }
        }
      else if (labels.size() > 0)
        {
        if (dict.contains(labels[0]))
          labels = dict.value(labels[0]);
        }
      (*this)[i].at(j)->setLabels(labels);
      }
    }
  }

/*
 * Sorting according to number of:
 * operators ->
 * functions (other than deltas) ->
 * deltas ->
 * variables
 */
bool Expression::sentenceLessThan(const Sentence &s1, const Sentence &s2)
  {
  if (s1.countWordType(Word::WT_OPERATOR) != s2.countWordType(Word::WT_OPERATOR))
    return s1.countWordType(Word::WT_OPERATOR) > s2.countWordType(Word::WT_OPERATOR);
  else if (s1.countWordType(Word::WT_FUNCTION) != s2.countWordType(Word::WT_FUNCTION))
    return s1.countWordType(Word::WT_FUNCTION) > s2.countWordType(Word::WT_FUNCTION);
  else if (s1.countWordType(Word::WT_DELTA) != s2.countWordType(Word::WT_DELTA))
    return s1.countWordType(Word::WT_DELTA) > s2.countWordType(Word::WT_DELTA);
  else if (s1.countWordType(Word::WT_VARIABLE) != s2.countWordType(Word::WT_VARIABLE))
    return s1.countWordType(Word::WT_VARIABLE) > s2.countWordType(Word::WT_VARIABLE);
  else if (s1.size() != s2.size())
    return s1.size() < s2.size();
  else // in case sentence has the same number of operators, functions, deltas, variables and size
    return s2.isGreaterThan(s1);
  }

Expression::~Expression()
  {
  }
